package com.shuiyun.jtt.utils;

import cn.hutool.core.codec.BCD;
import cn.hutool.core.util.StrUtil;
import io.netty.buffer.*;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
 * @author shuiyun
 * @program jtt1078
 * @description Byte读取工具类
 * @date 2021-09-04 11:03
 **/
@Slf4j
public class ByteUtil {

    /**
     * 返回一个bcd 高四位为一个数字 第四位为一个数字
     *
     * @param b 字节
     * @return bcd字符串
     */
    public static String getBcd(byte b) {
        int ch1 = (b >> 4) & 0x0f;
        int ch2 = (b & 0x0f);
        return ch1 + "" + ch2;
    }

    /**
    *
    * @param data 待转换数据
    * @return  字节数组
    * @author shuiyun
    * @date  2021/9/7
    */
    public static byte[] getThreeBytes(Integer data){
        byte[] bytes = new byte[3];
        bytes[0] = (byte)((data >> 16) & 0xff);
        bytes[1] = (byte)((data >> 8) & 0xff);
        bytes[2] = (byte)(data & 0xff);
        return bytes;
    }

    public static byte[] initBytes(int size,byte content){
        byte[] bytes = new byte[size];
        for (int i=0;i<size;i++){
            bytes[i] = content;
        }
        return bytes;
    }

    /**
     * 数组转换成十六进制字符串
     *
     * @param array 字节数组
     * @return HexString
     */
    public static String bytes2HexStr(byte[] array) {
        if (null == array || array.length < 1 ){
            return "";
        }
        StringBuilder sb = new StringBuilder(array.length);
        String sTemp;
        for (byte b : array) {
            sTemp = Integer.toHexString(0xff & b);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 将ByteBuf转换成16进制字符串
     * @param buf 待转换的ByteBuf
     * @author shuiyun
     * @date 2022/4/18
     * @return 十六进制字符串
     */
    public static String bytes2HexStr(ByteBuf buf) {
        byte[] bytes = readReadableBytes(buf);
        return bytes2HexStr(bytes);
    }

    /**
     * 数组转换成带0x的十六进制字符串
     *
     * @param array
     * @return HexString
     */
    public static String bytes2FullHexStr(byte[] array) {
        StringBuilder sb = new StringBuilder(array.length);
        sb.append("0x");
        String sTemp;
        for (int i = 0; i < array.length; i++) {
            sTemp = Integer.toHexString(0xFF & array[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp);
            if (i < array.length - 1) {
                sb.append("0x");
            }
        }
        return sb.toString().toLowerCase();
    }


    /**
     * 把16进制字符串转换成字节数组
     *
     * @param hex 16进制字符串
     * @return byte[]
     */
    public static byte[] hexStr2Bytes(String hex) {
        if (StrUtil.isBlank(hex)){
            return new byte[0];
        }
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] chars = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            result[i] = (byte) (toByte(chars[pos]) << 4 | toByte(chars[pos + 1]));
        }
        return result;
    }

    /**
     * 相当于(short *) byte_pointer的效果
     * @param src 输入的字节数组
     * @author shuiyun
     * @date 2021-09-07 15:53
     * @return 字节数组
     */
    public static short[] toShortArray(byte[] src)
    {
        short[] dst = new short[src.length / 2];
        for (int i = 0, k = 0; i < src.length; )
        {
            dst[k++] = (short)((src[i++] & 0xff) | ((src[i++] & 0xff) << 8));
        }
        return dst;
    }

    /**
     * 读取bytebuf剩下的字节
     * @param msg 可读取bytebuf
     * @author shuiyun
     * @date 2021-09-07 16:15
     * @return 剩下所有的字节
     */
    public static byte[] readReadableBytes(ByteBuf msg) {
        byte[] content = new byte[msg.readableBytes()];
        msg.readBytes(content);
        return content;
    }

    private static int toByte(char c) {
        byte b = (byte) "0123456789ABCDEF".indexOf(c);
        return b;
    }

    /**
     * 根据字节码生成字节流
     * @param bytes 输入的字节码数组
     * @author shuiyun
     * @date 2022-04-07 10:27
     * @return byteBuf
     */
    public static ByteBuf getBuf(byte[] bytes) {
        ByteBuf buf = Unpooled.buffer();
        buf.writeBytes(bytes);
        return buf;
    }

    /**
     * 校验
     */
    public static boolean verify(ByteBuf buf) {
        byte checkCode = buf.getByte(buf.readableBytes() - 1);
        buf = buf.slice(0, buf.readableBytes() - 1);
        byte calculatedCheckCode = bcc(buf);
        return checkCode == calculatedCheckCode;
    }

    /**
     * BCC校验(异或校验)
     */
    public static byte bcc(ByteBuf byteBuf) {
        byte cs = 0;
        while (byteBuf.isReadable()) {
            cs ^= byteBuf.readByte();
        }
        byteBuf.resetReaderIndex();
        return cs;
    }

    /**
     * BCC校验(异或校验)
     */
    public static byte bcc(byte[] bytes) {
        byte cs = 0;
        for (byte aByte : bytes) {
            cs ^= aByte;
        }
        return cs;
    }


    /**
     * 数据拆分
     * @param source 原数据
     * @author shuiyun
     * @date 2022-04-20 16:54
     * @return 数据拆分结果
     */
    public static List<ByteBuf> slice(ByteBuf source, int size){
        int total = source.writerIndex() / size;
        List<ByteBuf> result = new ArrayList<>();
        int start = 0;
        for (int i = 0; i < total; i++) {
            result.add(source.slice(start,size));
            start += size;
        }
        if (source.writerIndex() > start){
            result.add(source.slice(start,source.writerIndex() - start));
        }
        return result;
    }

    /**
     * 数据源转义
     * 1、 0x7e -> 0x7d0x01
     * 2、 0x7e -> 0x7d0x02
     * @param source 源数据
     * @param oldByte 原字节
     * @param newByteBuf 新字节流
     * @author shuiyun
     * @date 2022/4/23 19:52
     * @return 转义好的数据
     */
    public static ByteBuf escape(ByteBuf buf,byte oldByte,ByteBuf newByteBuf){
        List<ByteBuf> list = new ArrayList<>();
        int index = buf.bytesBefore(oldByte);
        while (index > -1){
            if (index > 0){
                list.add(buf.readSlice(index));
            }
            buf.skipBytes(1);
            list.add(newByteBuf);
            index = buf.bytesBefore(oldByte);
        }
        list.add(buf.readSlice(buf.readableBytes()));
        return new CompositeByteBuf(UnpooledByteBufAllocator.DEFAULT, false, list.size(), list);
    }

    /**
     * 数据源反转义
     * @param source 源数据
     * @param oldByte 原字节数组 多字节转单字节
     * @param newByteBuf 新输入的数据
     * @author shuiyun
     * @date 2022/4/23 19:53
     * @return 反转义好的数据
     */
    public static ByteBuf unEscape(ByteBuf buf,byte[] oldByte,ByteBuf newByteBuf){
        ByteBuf tempBuf = Unpooled.wrappedBuffer(new byte[]{oldByte[0]});
        List<ByteBuf> list = new ArrayList<>();
        int index = buf.bytesBefore(oldByte[0]);
        while (index > -1){
            if (index > 0){
                list.add(buf.readSlice(index));
            }

            if (buf.readableBytes() >= oldByte.length){
                byte[] bytes = new byte[oldByte.length];
                buf.readBytes(bytes);
                if (bytesComp(bytes,oldByte)){
                    list.add(newByteBuf);
                }else{
                    buf.readerIndex(buf.readerIndex() - bytes.length);
                    buf.skipBytes(1);
                    list.add(tempBuf);
                }
            }else{
                buf.skipBytes(1);
                list.add(tempBuf);
            }
            index = buf.bytesBefore(oldByte[0]);
        }
        list.add(buf.readSlice(buf.readableBytes()));
        return new CompositeByteBuf(UnpooledByteBufAllocator.DEFAULT, false, list.size(), list);
    }

    /**
     * 字节数组对比
     * @param source 第一个数组
     * @param now 第二个字节数组
     * @author shuiyun
     * @date 2022/4/26 21:33
     * @return 对比是否一样
     */
    private static boolean bytesComp(byte[] source,byte[] now){
        if (source.length != now.length){
            return false;
        }
        String sourceStr = BCD.bcdToStr(source);
        String nowStr = BCD.bcdToStr(now);
        return sourceStr.equals(nowStr);
    }
}
